package sample.mybatis.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import sample.mybatis.dto.StudentDto;
import sample.mybatis.mapper.StudentMapper;

/**
 * @author : dongyz8
 * @Date : 2023/11/9 15:21
 **/
@Service
@Slf4j
public class ServiceA {

    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private ServiceB serviceB;

    /**
     * 实验1：A调用B，B抛异常并捕获，A、B是否都会回滚
     * 结果：A、B均不回回滚
     * 总结：因为A、B属于同一事务（propagation = Propagation.REQUIRED），只要A、B都不抛异常，则事务不会回滚；
     * 否则，只要A或B其中一个方法抛异常，则事务会回滚
     */
    @Transactional
    public void experience1(){
        StudentDto s1 = new StudentDto();
        s1.setAge(1);
        s1.setName("1");
        studentMapper.insert(s1);
        serviceB.experience1();

    }

    /**
     * 实验2：同一个类中的A调用B和C，B、C均使用@Transactional注解，B、C的@Transactional是否会失效
     * 结果：B、C的@Transactional会失效
     * 总结：只要是调用同一个类中的方法，都会使@Transactional失效
     */
    public void experience2(){
        log.info("====experience2 start!");
        StudentDto s1 = new StudentDto();
        s1.setAge(1);
        s1.setName("1");
        studentMapper.insert(s1);
        experience2B();
        experience2C();
//        serviceB.experience2();
        log.info("====experience2 end!");
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void experience2B(){
        log.info("====experience2B start!");
        StudentDto s1 = new StudentDto();
        s1.setAge(2);
        s1.setName("2");
        studentMapper.insert(s1);

        StudentDto s2 = new StudentDto();
        s2.setAge(3);
        s2.setName("3");
        studentMapper.insert(s2);
        log.info("====experience2B end!");
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void experience2C(){
        log.info("====experience2C start!");
        StudentDto s1 = new StudentDto();
        s1.setAge(3);
        s1.setName("3");
        studentMapper.insert(s1);
        log.info("====experience2C end!");
    }


    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    @Autowired
    private TransactionDefinition transactionDefinition;
    /**
     * 实验3：编程式事务
     */
    public void experience3(){
        //方式1：使用默认的事务配置来开启事务（
        TransactionStatus transactionStatus = dataSourceTransactionManager
                .getTransaction(transactionDefinition);

        //方式2：自己创建配置，可以设置事务传播机制,但一般都是使用默认即可PROPAGATION_REQUIRED。
//        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
//        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); //传播机制
//        transactionStatus = dataSourceTransactionManager.getTransaction(def);

        //getTransaction后，事务自动开始
        try {
            StudentDto s1 = new StudentDto();
            s1.setAge(1);
            s1.setName("1");
            studentMapper.insert(s1);

            StudentDto s2 = new StudentDto();
            s2.setAge(2);
            s2.setName("2");
            studentMapper.insert(s2);


            dataSourceTransactionManager.commit(transactionStatus); // 手动提交
        } catch (Exception e) {
            System.out.println("异常——————————————————");
            dataSourceTransactionManager.rollback(transactionStatus); // 事务回滚
            e.printStackTrace();
        }
    }

    /**
     * 实验4：A调用B，B抛异常不处理，A处理B的异常，是否会抛异常：Transaction rolled back because it has been marked as rollback-only
     * 结果：是的
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void experience4(){
        try{
            StudentDto s1 = new StudentDto();
            s1.setAge(1);
            s1.setName("1");
            studentMapper.insert(s1);

            serviceB.experience4();
        }catch (Exception e){
            log.error("serviceB异常被捕获",e);
        }

    }

    /**
     * 实验5：死锁示例。
     * A insert一条记录后，调用B；B新开一个事务，update该记录；
     * 因为A持有该记录的锁，且A事务未完结，B只能一直等待；B取锁超时后便抛出死锁异常。
     */
    @Transactional
    public void experience5(){
        StudentDto s1 = new StudentDto();
        s1.setAge(1);
        s1.setName("1");
        studentMapper.insert(s1);

        serviceB.experience5(s1);
    }
}
